/*
 * proc.c
 *
 *  Created on: 2016-11-11
 *      Author: jared
 */

#include "constants.h"
#include "types.h"
#include "defs.h"
#include "mm.h"
#include "x86.h"
#include "proc.h"

struct {
	uint lock;
	struct proc proc[NPROC];
} ptable;

int nextpid = 1;
extern void trapret(void);

void pinit(void) {
	ptable.lock = 0;
}

void scheduler(void) {
	struct proc *p;
	sti();
	for (;;) {
		acquire(&ptable.lock);
		for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) {
			if (p->state != RUNNABLE)
				continue;

			proc = p; //关键代码，proc相当于linux0.11里的current，指向当前执行的进程
			switchUVM(p);
			p->state = RUNNING;
			swtch(&cpu->scheduler, proc->context);
			switchKVM();

			proc = 0; //此时处于内核进程，proc置null,trap.c里面会用到proc判空操作，
					  //如果为空（表示处于内核进程中）有些操作就不做了
		}
		release(&ptable.lock);

	}
}

void userinit(void) {
	struct proc *p;
	extern char _binary_initcode_start[], _binary_initcode_size[];

	p = allocproc();
	p->pgdir = setupKVM();
	initUVM(p->pgdir, _binary_initcode_start, (int) _binary_initcode_size);
	p->sz = PGSIZE;
	memset(p->tf, 0, sizeof(*p->tf));
	p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
	p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
	p->tf->es = p->tf->ds;
	p->tf->ss = p->tf->ds;
	p->tf->eflags = FL_IF;
	p->tf->esp = PGSIZE;
	p->tf->eip = 0;  // beginning of initcode.S

	strncpyEnd0(p->name, "initcode", sizeof(p->name));

	p->state = RUNNABLE;
}

int fork(void) {
	int pid;
	struct proc *np;

	// Allocate process.
	if ((np = allocproc()) == 0)
		return -1;

	// Copy process state from p.
	if ((np->pgdir = copyUVM(proc->pgdir, proc->sz)) == 0) {
		kFreePage(np->kstack);
		np->kstack = 0;
		np->state = UNUSED;
		return -1;
	}
	np->sz = proc->sz;
	np->parent = proc;
	*np->tf = *proc->tf;

	// Clear %eax so that fork returns 0 in the child.
	np->tf->eax = 0;

	strncpyEnd0(np->name, proc->name, sizeof(proc->name));

	pid = np->pid;

	// lock to force the compiler to emit the np->state write last.
	acquire(&ptable.lock);
	np->state = RUNNABLE;
	release(&ptable.lock);

	return pid;
}

struct proc* allocproc(void) {
	struct proc *p;
	char *sp;

	acquire(&ptable.lock);
	for (p = ptable.proc; p < &ptable.proc[NPROC]; p++)
		if (p->state == UNUSED)
			goto found;
	release(&ptable.lock);
	return 0;

	found: p->state = EMBRYO;
	p->pid = nextpid++;
	release(&ptable.lock);

	if ((p->kstack = kAlloc()) == 0) {
		p->state = UNUSED;
		return 0;
	}
	sp = p->kstack + KSTACKSIZE;

	// Leave room for trap frame.
	sp -= sizeof *p->tf;
	p->tf = (struct trapframe*) sp;

	sp -= 4;
	*(uint*) sp = (uint) trapret;

	sp -= sizeof *p->context;
	p->context = (struct context*) sp;
	memset(p->context, 0, sizeof *p->context);
	p->context->eip = (uint) forkret;

	return p;
}

void forkret(void) {
	release(&ptable.lock);
}

void sched(void) {
	swtch(&proc->context, cpu->scheduler);
}

void yield(void) {
	acquire(&ptable.lock);
	proc->state = RUNNABLE;
	sched();
	release(&ptable.lock);
}

void procdump(void) {
	static char *states[] = {
			[UNUSED]   "unused",
			[EMBRYO]   "embryo",
			[RUNNABLE] "runble",
			[RUNNING]  "run   " };
	struct proc *p;
	char *state;

	for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) {
		if (p->state == UNUSED)
			continue;
		if (p->state >= 0 && p->state < LENGTH(states) && states[p->state])
			state = states[p->state];
		else
			state = "???";
		printk("%d %s %s", p->pid, state, p->name);
		printk("\n");
	}
}
